/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { assert, beforeAll, describe, test } from 'vitest';
import { BadParameterPassingCheck } from '../../../src/checker/performance/BadParameterPassingCheck';
import { CHECK_MODE, testCaseCheck } from './common/testCommon';
import path from 'path';
import { CheckEntry, Rule } from '../../../src/Index';
import { FileIssues } from '../../../src/model/Defects';

const SAMPLE_DIR = 'test/unittest/sample/BadParameterPassing/ets';
let realPath: string = '';
let checkEntry: CheckEntry;
let rule: Rule;

beforeAll(async () => {
  rule = new Rule('performance/bad-parameter-passing-check', 1);
  checkEntry = await testCaseCheck(SAMPLE_DIR, rule, CHECK_MODE.PROJECT2CHECK, BadParameterPassingCheck);
  realPath = checkEntry.scene.getRealProjectDir();
});

function findFileCheck(fileIssues: FileIssues[], detectFile: string): FileIssues | undefined {
  // 统一路径分隔符，兼容 Windows/Linux
  const normDetectFile = path.normalize(detectFile);
  return fileIssues.find(f => path.normalize(f.filePath) === normDetectFile);
}

describe('BadParameterPassingCheck', () => {
  test('should report warning for @State+@Prop chain depth >=3', () => {
    const detectFile = path.join(realPath, 'BadParameterPassingReport.ets');
    const fileIssues = checkEntry.sortIssues();
    const fileCheck = findFileCheck(fileIssues, detectFile);
    assert.isDefined(fileCheck, `The file path is error. fileCheck not found for ${detectFile}`);
    if (!fileCheck) return;
    assert.isAbove(fileCheck.issues.length ?? 0, 0, 'Should report at least one issue.');
    assert.match(fileCheck.issues[0].defect.description ?? '', /Detects @State\+@Prop, @State\+@Link, or @State\+@Observed\+@ObjectLink on the same field/);
    // 补充位置信息断言
    const defect = fileCheck.issues[0].defect;
    const defectFilePath = defect.mergeKey.split('%')[0];
    assert.equal(defectFilePath, detectFile);
    assert.equal(defect.reportLine, 5);
    assert.equal(defect.reportColumn, 3);
  });

  test('should not report warning for @State+@Prop chain depth <3', () => {
    const detectFile = path.join(realPath, 'BadParameterPassingNoReport.ets');
    const fileIssues = checkEntry.sortIssues();
    const fileCheck = findFileCheck(fileIssues, detectFile);
    // 没有问题时，fileCheck 可能为 undefined
    assert.isTrue(!fileCheck || (fileCheck.issues.length === 0), 'Should not report any issues.');
    if (!fileCheck) return;
  });

  test('should report warning for @State+@Link chain depth >=3', () => {
    const detectFile = path.join(realPath, 'BadParameterPassingLinkReport.ets');
    const fileIssues = checkEntry.sortIssues();
    const fileCheck = findFileCheck(fileIssues, detectFile);
    assert.isDefined(fileCheck, `The file path is error. fileCheck not found for ${detectFile}`);
    if (!fileCheck) return;
    assert.isAbove(fileCheck?.issues.length ?? 0, 0, 'Should report at least one issue.');
    // familyName, familyMotto 都应被告警
    const defect1 = fileCheck.issues[0].defect;
    const defect2 = fileCheck.issues[1].defect;
    // familyName
    const defectFilePath1 = defect1.mergeKey.split('%')[0];
    assert.equal(defectFilePath1, detectFile);
    assert.equal(defect1.reportLine, 4);
    assert.equal(defect1.reportColumn, 3);
    // familyMotto
    const defectFilePath2 = defect2.mergeKey.split('%')[0];
    assert.equal(defectFilePath2, detectFile);
    assert.equal(defect2.reportLine, 5);
    assert.equal(defect2.reportColumn, 3);
  });

  test('should report warning for @State+@Observed+@ObjectLink chain depth >=3', () => {
    const detectFile = path.join(realPath, 'BadParameterPassingObservedObjectLinkReport.ets');
    const fileIssues = checkEntry.sortIssues();
    const fileCheck = findFileCheck(fileIssues, detectFile);
    assert.isDefined(fileCheck, `The file path is error. fileCheck not found for ${detectFile}`);
    if (!fileCheck) return;
    assert.isAbove(fileCheck?.issues.length ?? 0, 0, 'Should report at least one issue.');
    assert.match(fileCheck?.issues[0].defect.description ?? '', /Detects @State\+@Prop, @State\+@Link, or @State\+@Observed\+@ObjectLink on the same field/);
    // 补充位置信息断言
    const defect = fileCheck.issues[0].defect;
    const defectFilePath = defect.mergeKey.split('%')[0];
    assert.equal(defectFilePath, detectFile);
    assert.equal(defect.reportLine, 132);
    assert.equal(defect.reportColumn, 3);
  });
});
